/**
 * Captain
 * © 2017 Captain copyright，All rights reserved.
 * http://www.sccaptain.com.cn
 * 
 * JAVA : 8
 * 文  件  名: HqlHelper.java
 * 创  建  人: liutao (liu.tao@sccaptain.com.cn)
 * 创建时间: 2017年8月2日 下午3:08:53
 * 版         本: 1.0.0
 * 备         注:
 * 修订历史:
 */
package com.material.c2c.core.criteria;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Sort.Order;
import org.springframework.util.StringUtils;

import com.material.c2c.core.criteria.Criterion.MatchMode;
import com.material.c2c.core.criteria.CustomExpression.FieldType;
import com.material.c2c.core.criteria.CustomExpression.Operator;

/**
 * 
 * @since 1.0
 * @version 1.0
 * @author liutao (liu.tao@sccaptain.com.cn)
 */
public class CriteriaHelper {
  
  private Logger logger = LoggerFactory.getLogger(CriteriaHelper.class);

  private StringBuffer whereClause = new StringBuffer();// Where子句
  private StringBuffer orderClause = new StringBuffer();// order子句

  private List<String> strList = new ArrayList<>();
  private List<Object> valueList = new ArrayList<>();
  
  private String distinct;
  
  private  int j = 1; //占位符
  
  private  boolean flag = false;
  
  private PageInfo pageInfo;

  private QueryParams params;

  public CriteriaHelper(QueryParams params) {
    this.params = params;
    this.generate();
  }

  public void generate() {
    if (StringUtils.isEmpty(this.params))
      return;
    int i = 1;
    List<CriterionExpression> expressions = null;
    // 解析 表达式
    for (CompoundExpression expression : this.params.getExpressions()) {
      expressions = expression.getExpressions();
      for (CriterionExpression exp : expressions) {
        if (checkValue(exp.getValue())) {
          // 设置
          toExpression(exp,i++);
        } else {
          // is null/is not null 例外
          if (exp.getOperator().getOperator().equals(Criterion.Operator.ISNULL.getOperator())
              || exp.getOperator().getOperator().equals(Criterion.Operator.ISNOTNULL.getOperator())) {
            strList.add(exp.getProperty() + " " + exp.getOperator().getOperator());
          }
        }
      }
      // 拼接HQL
      if (!strList.isEmpty()) {
        if(whereClause.length()>0){
          whereClause.append(" and ");
        }else{
          whereClause.append(" where ");
        }
        whereClause.append("( ").append(org.apache.commons.lang3.StringUtils.join(strList, expression.getBooleanOperator().getBooleanOperator()))
            .append(") ");
      }
      strList.clear();
    }

    // 解析排序字段
    if (!this.params.getOrders().isEmpty()) {
      List<String> orders = new ArrayList<>();
      this.params.getOrders().forEach(order -> {
        orders.add(order.getProperty() + " " + order.getDirection());
      });
      if (orders.size() > 0) {
        orderClause.append(" order by ");
        orderClause.append(org.apache.commons.lang3.StringUtils.join(orders, ","));
      }
    }
    
    // 统计去重复
    this.distinct = this.params.getDistinct();
  }

  /**
   * 判断值是否为空值或空字符串
   * 
   * @param value
   * @return boolean true 不为空 false 为空
   * @since 1.0
   * @version 1.0
   * @author liutao (liu.tao@sccaptain.com.cn)
   */
  private boolean checkValue(Object value) {
    boolean bool = false;
    if (!StringUtils.isEmpty(value)) {
      if (value instanceof String) {
        if (StringUtils.hasText(value.toString())) {
          bool = true;
        }
      } else {
        bool = true;
      }
    }
    return bool;
  }
  
  // 设置属性值
  private void toExpression(CriterionExpression exp,int i){
    StringBuffer temp = new StringBuffer();
    if (Criterion.Operator.LIKE.equals(exp.getOperator())) {
      if (MatchMode.LEFT.equals(exp.getMode())) {
        valueList.add("%" + exp.getValue());
      } else if (MatchMode.RIGHT.equals(exp.getMode())) {
        valueList.add(exp.getValue() + "%");
      } else {
        valueList.add("%" + exp.getValue() + "%");
      }
      temp.append(exp.getProperty()).append(" ").append(exp.getOperator().getOperator()).append(" ?").append(j++);
    }else if (Criterion.Operator.IN.equals(exp.getOperator())){
      Object[] os = (Object[])exp.getValue();
      valueList.add(org.apache.commons.lang3.StringUtils.join(os, ","));
      temp.append(exp.getProperty()).append(" ").append(exp.getOperator().getOperator()).append("( ?").append(j++).append(" )");
    }else {
      temp.append(exp.getProperty()).append(" ").append(exp.getOperator().getOperator()).append(" ?").append(j++);
      valueList.add(objToJavaType(exp.getValue(), exp.getProperty(), exp.getPattern()));
    }
    strList.add(temp.toString());
  }
  
  public CriteriaHelper(PageInfo pageInfo){
    this.pageInfo = pageInfo;
    this.toResolve(pageInfo.getFilters());
  }
  
  /**
   * 根据表达式 Expression 生成where 条件。
   * @param expressions
   * @since 1.0
   * @version 1.0
   * @author liutao (liu.tao@sccaptain.com.cn)
   */
  private void toResolve(CustomExpression[] expressions) {
    if(StringUtils.isEmpty(expressions)) return;
    CustomExpression expression = null;
    for (int i = 0; i < expressions.length; i++) {
      expression = expressions[i];
      toResolveExpression(expressions, expression, i);
      // 递归
      if (!StringUtils.isEmpty(expression.getChildren()) && expression.getChildren().length > 0) {
        toResolve(expression.getChildren());
      }
      // 判断是否有表达式
      isExpression(expression);
      if (i == (expressions.length - 1)&&flag) { 
        whereClause.append(" ) ");
        flag=false;
      }
    }
  }

  /**
   * 判断表达式及其子集是否可用。
   * 
   * @param expression
   * @since 1.0
   * @version 1.0
   * @author liutao (liu.tao@sccaptain.com.cn)
   */
  private void isExpression(CustomExpression expression){
    if(StringUtils.hasText(expression.getField()) && !StringUtils.isEmpty(expression.getValue())){
      flag = true;
    }
    if (!StringUtils.isEmpty(expression.getChildren()) && expression.getChildren().length > 0) {
      for(int i=0;i<expression.getChildren().length;i++){
        isExpression(expression.getChildren()[i]);
      }
    }
  }
  
  /**
   * 拼装where 条件
   * @param expressions
   * @param expression
   * @param i
   * @since 1.0
   * @version 1.0
   * @author liutao (liu.tao@sccaptain.com.cn)
   */
  private void toResolveExpression(CustomExpression[] expressions,CustomExpression expression,int i){
    CustomExpression expression2 = null;
    // 判断属性和值 是否为空 如果字段或值为空 不需要该条件 跳过执行。
    if (StringUtils.hasText(expression.getField()) && !StringUtils.isEmpty(expression.getValue()))  {
      // i=0 直接拼接属性
      if (i == 0) {
        if (whereClause.length() > 0) {
          whereClause.append(" and ( ");
        } else {
          whereClause.append(" where ( ");
        }
      } else { 
        // 判断是否需要拼装运算符，如果前面遍历的要素都没拼接，那么需要拼接运算符默认 为and
        int m = i - 1;
        boolean bool = false;
        while (m >= 0) {
          expression2 = expressions[m];
          if (StringUtils.hasText(expression2.getField()) && !StringUtils.isEmpty(expression2.getValue())) {
            bool = true;
            break;
          }
          m--;
        }
        
        if (bool) {
          if(StringUtils.hasText(expression.getLogic())){
            whereClause.append(" ").append(expression.getLogic()).append(" ");
          }else{
            whereClause.append(" and ");
          }
        } else {
          if (whereClause.length() > 0) {
            whereClause.append(" and ( ");
          } else {
            whereClause.append(" where ( ");
          }
        }
      }
      // 解析运算符、初始值
      toOperator(expression);
    }
  }
  
  /**
   * 解析运算符
   * 
   * @since 1.0
   * @version 1.0
   * @author liutao (liu.tao@sccaptain.com.cn)
   */
  private void toOperator(CustomExpression expression){
    if(Operator.CONTAINS.getOperator().equals(expression.getOperator())){
      whereClause.append(" ").append(expression.getField()).append(" like ").append(" ?").append(j++);
      valueList.add("%"+expression.getValue()+"%");
    }else if(Operator.STARTSWITH.getOperator().equals(expression.getOperator())){
      whereClause.append(" ").append(expression.getField()).append(" like ").append(" ?").append(j++);
      valueList.add("%"+expression.getValue());
    }else if(Operator.ENDWITH.getOperator().equals(expression.getOperator())){
      whereClause.append(" ").append(expression.getField()).append(" like ").append(" ?").append(j++);
      valueList.add(expression.getValue()+"%");
    }else{
      whereClause.append(" ").append(expression.getField()).append(" ").append(expression.getOperator()).append(" ?").append(j++);
      valueList.add(objToJavaType(expression.getValue(),expression.getFieldType(),expression.getPattern()));
    }
  } 
  
  /**
   * 排序字段。 
   * 
   * @since 1.0
   * @version 1.0
   * @author liutao (liu.tao@sccaptain.com.cn)
   */
  public void toResolveOrder(){  
    List<Order> orders = new ArrayList<>();
    pageInfo.getPage().createPageable().getSort().forEach(order -> {
      orders.add(order);
    });
    
    // 解析排序字段
    if (!orders.isEmpty()) {
      List<String> orders2 = new ArrayList<>();
      orders.forEach(order -> {
        orders2.add(order.getProperty() + " " + order.getDirection());
      });
      if (orders.size() > 0) {
        orderClause.append(" order by ");
        orderClause.append(org.apache.commons.lang3.StringUtils.join(orders, ","));
      }
    }
  }
  
  public boolean isPropertyExist = false;
  
  public void isPropertyExist(String property,CustomExpression[] expressions){
    if(StringUtils.isEmpty(expressions)){
      isPropertyExist = false;
      return;
    }
    CustomExpression expression = null;
    for(int i=0;i<expressions.length;i++){
      expression = pageInfo.getFilters()[i];
      if (StringUtils.hasText(expression.getField()) && !StringUtils.isEmpty(expression.getValue())&& expression.getField().equals(property))  {
        isPropertyExist = true;
        break;
      }
      if(!StringUtils.isEmpty(expression.getChildren())){
        isPropertyExist(property, expression.getChildren());
      }
    }
  }

  public CriteriaHelper(QueryParams params,PageInfo pageInfo){
    this.params = params;
    this.pageInfo = pageInfo;
    this.generate();
    this.toResolve(pageInfo.getFilters());
  }
  
  public Object objToJavaType(Object obj,String type,String pattern){
    if(StringUtils.hasText(type)){
      if(type.equals(FieldType.INT.getFieldType())){
        return Integer.parseInt(obj.toString());
      }else if(type.equals(FieldType.FLOAT.getFieldType())){
        return Float.parseFloat(obj.toString());
      }else if(type.equals(FieldType.DOUBLE.getFieldType())){
        return Double.parseDouble(obj.toString());
      }else if(type.equals(FieldType.DATE.getFieldType())){
        try {
          if(!StringUtils.hasText(pattern)){
            pattern = "yyyy-MM-dd";
          }
          return new SimpleDateFormat(pattern).parse(obj.toString());
        } catch (ParseException e) {
          logger.error("日期类型转换出错：", e);
          return null;
        }
      }else{
        return obj;
      }
    }else{
      return obj;
    }
    
  }
  
  public StringBuffer getWhereClause() {
    return whereClause;
  }

  public void setWhereClause(StringBuffer whereClause) {
    this.whereClause = whereClause;
  }

  public StringBuffer getOrderClause() {
    return orderClause;
  }

  public void setOrderClause(StringBuffer orderClause) {
    this.orderClause = orderClause;
  }

  public List<Object> getValueList() {
    return valueList;
  }

  public void setValueList(List<Object> valueList) {
    this.valueList = valueList;
  }

  public String getDistinct() {
    return distinct;
  }

  public void setDistinct(String distinct) {
    this.distinct = " distinct "+ distinct;
  }
}
